﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Mathematics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Dynamics.Springs;
using FarseerGames.FarseerPhysics.Factories;

namespace Demo5
{
  public partial class Page : UserControl
  {
    private Storyboard gameLoop;
    private PhysicsSimulator physicsSimulator;

    //The entity you can control via the keyboard
    private PhysicsBox playerBox;
    private List<IUpdateable> entityList = new List<IUpdateable>();

    private Geom pickedGeom;
    private FixedLinearSpring mousePickSpring;

    public Page()
    {
      //Default Component Initializer created by VS2008, just leave it :)
      InitializeComponent();

      this.Loaded += new RoutedEventHandler(Page_Loaded);
      this.MouseLeftButtonDown += new MouseButtonEventHandler(Page_MouseLeftButtonDown);
      this.MouseLeftButtonUp += new MouseButtonEventHandler(Page_MouseLeftButtonUp);
      this.MouseMove += new MouseEventHandler(Page_MouseMove);
    }

   

    protected void Page_Loaded(object sender, RoutedEventArgs e)
    {
      //Setup GameLoop
      gameLoop = new Storyboard();
      gameLoop.Completed += new EventHandler(gameLoop_Completed);
      gameLoop.Duration = TimeSpan.FromMilliseconds(15); //The game will be updated every 15 millisecond
      this.Resources.Add("gameloop", gameLoop);

      //Setup PhysicsSimulator - No gravity applied this time
      physicsSimulator = new PhysicsSimulator(new Vector2(0, 1000));


      //Add our PhysicsBox (PhysicsElement, Sprite, what ever you wanna call it)
      playerBox = new PhysicsBox(150, 150, 100, Color.FromArgb(255,75, 150, 210));
      playerBox.Body.Position = new Vector2((float)this.Width / 2, (float)this.Height / 2);
      //Setup physical appearance for the playerBox. This will set the box physical boundaries for the 
      //playerBox so the PhysicsSimulator knows how the box should behave.
      Vertices playerVertices = Vertices.CreateRectangle((float)playerBox.Width, (float)playerBox.Height);
      Geom playerGeometry = new Geom(playerBox.Body, playerVertices, Vector2.Zero, 0, 12.8f); //12.8f taken from Demo2 in Farseer. Need a fix here as I dunno what it's doing :)

      //Setup a dummy box to test against
      PhysicsBox dummyBox = new PhysicsBox(100, 100, 2000);
      dummyBox.Body.Position = new Vector2((float)playerBox.Body.Position.X, ((float)playerBox.Body.Position.Y - (float)dummyBox.Height));
      Vertices dummyVertices = Vertices.CreateRectangle((float)dummyBox.Width, (float)dummyBox.Height);
      Geom dummyGeometry = new Geom(dummyBox.Body, dummyVertices, Vector2.Zero, 0, 12.8f); //12.8 taken from Demo2 in Farseer. Need a fix here.

      //Setup floor
      PhysicsBox floor = new PhysicsBox(800, 25, float.MaxValue, Color.FromArgb(255,10,10,10));
      floor.Body.Position = new Vector2(400, 590);
      floor.Body.IgnoreGravity = true; //We don't want the floor to move at all
      Vertices floorVertices = Vertices.CreateRectangle((float)floor.Width, (float)floor.Height);
      Geom floorGeometry = new Geom(floor.Body, floorVertices, Vector2.Zero, 0, 12.8f); //12.8 taken from Demo2 in Farseer. Need a fix here.
      floorGeometry.RestitutionCoefficient = 1.0f;
      floorGeometry.FrictionCoefficient = 1.0f;

      //Add the Body of the boxes here, so it will be affected by the physicsSimulator.
      physicsSimulator.Add(playerBox.Body);
      physicsSimulator.Add(dummyBox.Body);
      physicsSimulator.Add(floor.Body);

      //Adds the geometry for the boxes to the simulator too
      physicsSimulator.Add(playerGeometry);
      physicsSimulator.Add(dummyGeometry);
      physicsSimulator.Add(floorGeometry);

      //Add the actual PhysicsBox(UserControl) object to the list of UserControls to be updated in the gameloop event
      entityList.Add(playerBox);
      entityList.Add(dummyBox);
      entityList.Add(floor);

      //Finally we add the PhysicsBox (UserControl) to the actual canvas where we want it to be rendered, so we can actually see it :)
      //We also add the dummyBox, else we will never see it on the canvas
      this.DrawingCanvas.Children.Add(playerBox);
      this.DrawingCanvas.Children.Add(dummyBox);
      this.DrawingCanvas.Children.Add(floor);

      //This will start the actual gameloop - this is where your application actually starts (from the users point of view)
      gameLoop.Begin();
    }

    protected void gameLoop_Completed(object sender, EventArgs e)
    {
      //Update the physics
      physicsSimulator.Update(.01f);

      //Update every entity in the game
      foreach (IUpdateable box in entityList)
      {
        box.Update();
      }

      //Draw debug information
      #if DEBUG
          DrawVertices();
      #endif

      gameLoop.Begin();
    }


    #region Mouse Events

    protected void Page_MouseMove(object sender, MouseEventArgs e)
    {
      if (mousePickSpring != null)
      {
        Vector2 point = new Vector2((float)(e.GetPosition(this).X), (float)(e.GetPosition(this).Y));
        mousePickSpring.WorldAttachPoint = point;
      }
    }

    protected void Page_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
      if (mousePickSpring != null && mousePickSpring.IsDisposed == false)
      {
        mousePickSpring.Dispose();
        mousePickSpring = null;
      }
    }

    protected void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      Vector2 point = new Vector2((float)(e.GetPosition(this).X), (float)(e.GetPosition(this).Y));
      pickedGeom = physicsSimulator.Collide(point);
      if (pickedGeom != null)
      {
        mousePickSpring = ControllerFactory.Instance.CreateFixedLinearSpring(physicsSimulator, pickedGeom.Body, pickedGeom.Body.GetLocalPosition(point), point, 250, 100);
      }
    }

    #endregion

    //http://www.cameronalbert.com/post.aspx?id=1b69e6be-8a2d-49be-8d63-716e54dd1f61
    private void DrawVertices()
    {
      _debug.Children.Clear();
      int verticeCount = 0;
      for (int i = 0; i < physicsSimulator.GeomList.Count; i++)
      {
        verticeCount = physicsSimulator.GeomList[i].LocalVertices.Count;
        for (int j = 0; j < verticeCount; j++)
        {
          Line line = new Line();
          line.Fill = new SolidColorBrush(Colors.Transparent);
          line.Stroke = new SolidColorBrush(Colors.Magenta);
          line.StrokeThickness = 1;
          if (j < verticeCount - 1)
          {
            line.X1 = physicsSimulator.GeomList[i].WorldVertices[j].X;
            line.Y1 = physicsSimulator.GeomList[i].WorldVertices[j].Y;
            line.X2 = physicsSimulator.GeomList[i].WorldVertices[j + 1].X;
            line.Y2 = physicsSimulator.GeomList[i].WorldVertices[j + 1].Y;
          }
          else
          {
            line.X1 = physicsSimulator.GeomList[i].WorldVertices[j].X;
            line.Y1 = physicsSimulator.GeomList[i].WorldVertices[j].Y;
            line.X2 = physicsSimulator.GeomList[i].WorldVertices[0].X;
            line.Y2 = physicsSimulator.GeomList[i].WorldVertices[0].Y;
          }
          _debug.Children.Add(line);
        }
      }
    }

  }
}
